package com.pinapel.aixml.models.xml;

import java.util.ArrayList;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.pinapel.aixml.Logger;


/** 
 * @author karloz
 * @since 11/2/2012 
 */

public class DomModel {
	
	/*
	 * Members
	 */
	private Node mTree;	
	
	/**
	 * @param tree DOM root node
	 */
	public DomModel(Node tree){
		this.mTree = tree;
		
	}
	
	/**
	 * Creates an empty DomModel
	 */
	public DomModel() {
		 try {
		        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		        mTree = builder.newDocument();

		    } catch (ParserConfigurationException e) {
		    	System.err.println("meec");
		    }
	}
	
	/**
	 * @param  id Integer sequence that denotes the node's position inside DOMTree
	 * @return Node (DomTree node)
	 */
	public DomNode getNode(NodeID id){
		int head = 0;
		int index = 0;
		Node root = mTree;
		
		for (Iterator<Integer> it = id.getList().iterator();it.hasNext();){
			Node child = root.getFirstChild();
			head = it.next();
			while(child!=null && index < head){
				child = child.getNextSibling();
				index++;
			}
			index = 0;
			root = child;
		}
		
		if (root==null){
			Logger.log(" (DomNode.getNode) nodo obtenido nulo.");
		}		
		return new DomNode(root);		
	}
	
	/**
	 * @param id Integer sequence that denotes the node's position inside DOMTree
	 * @return short node type
	 * <p><ul>ATTRIBUTE_NODE</ul>        
		  <ul>CDATA_SECTION_NODE</ul>   
          <ul>COMMENT_NODE</ul>   
          <ul>DOCUMENT_FRAGMENT_NODE</ul>   
          <ul>DOCUMENT_NODE</ul>   
          <ul>DOCUMENT_TYPE_NODE</ul>   
          <ul>ELEMENT_NODE</ul>   
          <ul>ENTITY_NODE</ul>   
          <ul>ENTITY_REFERENCE_NODE</ul>   
          <ul>NOTATION_NODE</ul>   
          <ul>PROCESSING_INSTRUCTION_NODE</ul>   
          <ul>TEXT_NODE</ul>   
         </p>
	 */
	public short getType(NodeID id){		
		return this.getNode(id).getDomNode().getNodeType();
		
	}
	
	/**	 
	 * @param id Integer sequence that denotes the node's position inside DOMTree
	 */
	public void deleteNode(NodeID id){
		int last = id.getLast();
		id.getList().remove(id.getList().size()-1);
		DomNode father = this.getNode(id); 
		
		ArrayList<Integer> newNodeId = new ArrayList<Integer>();
		newNodeId.add(last);
		
		DomNode child = father.getNode(new NodeID(newNodeId));		
		
		if (father.getDomNode().removeChild(child.getDomNode())==null){
			System.out.println("Error al borrar");
		}
	}
	
	/**	  
	 * @param id Integer sequence that denotes the node's position inside DOMTree.
	 * @return DomNode A copy of the node specified by id.
	 */
	public DomNode copyNode(NodeID id){
		return this.getNode(id).clone();
	}
	
	
	public DomNode copyNodeDeep(NodeID id){
		return this.getNode(id).cloneDeep();
	}
	
	/**
	 * 
	 * @param node Node that need to be inserted
	 * @param position Integer sequence that denotes the node's position inside DOMTree.
	 */
	public void insertNode(DomNode node,NodeID position){
		int last = (position.getLast()!=0)? position.getLast() : 0;
		position.getList().remove(position.getList().size()-1);
		DomNode dNode = this.getNode(position);
		NodeList children = dNode.getDomNode().getChildNodes();
		if (last==children.getLength()){
			last--;
		}
		if (children.item(last)==null){
			dNode.getDomNode().appendChild(node.getDomNode());
		}else{
			dNode.getDomNode().insertBefore(node.getDomNode(), children.item(last));
		}
	}
	
	/**
	 * 
	 * @return DomNode text representation
	 */
	
	public String getTextVersion(){
		DomNode n = new DomNode(mTree);
		StringBuilder s = new StringBuilder();
		s.append((new DomNode(mTree)).getTextVersion());
		if(n.getDomNode().getNodeType()==Document.DOCUMENT_NODE) {
			Node tmp;
			tmp = n.getDomNode().getFirstChild();
			while (tmp!=null){
				s.append(new DomNode(tmp).getTextVersion());
				tmp = tmp.getNextSibling();
			}
		}
		return s.toString();
	}
	
	/**	 
	 * @param id Node to be moved
	 * @param position Final position
	 */
	
	public void moveNode(NodeID id,NodeID position){
		DomNode tmpNode = this.getNode(id);
		this.insertNode(tmpNode, position);
	}
	
	/**	 
	 * @return Node instance associated with this DomModel instance. 
	 */
	public Node getDomNode(){
		return this.mTree;
	}
	
	/**
	 * @return new DomModel cloned instance 
	 */
	public DomModel clone(){
		return new DomModel(this.mTree.cloneNode(false));
	}

	/**
	 * @return new DomModel deep cloned instance (his childs are cloned too) 
	 */
	public DomModel cloneDeep(){
		return new DomModel(this.mTree.cloneNode(true));
	}
	
	/** 
     * @param id Position inside DOMTree
     * @return String array that contains a string representation of id childs.
     */
    public String[] childsToString(NodeID id){
            String[] data;
            NodeList nodeList;
            if (id.getList().size()==0){
                    nodeList = ((Document)this.mTree).getChildNodes();                      
            }else{          
                    nodeList = this.getNode(id).getDomNode().getChildNodes();
            }
                    
            data = new String[nodeList.getLength()];
            for (int i=0;i<nodeList.getLength();i++){
                    if (((Node)nodeList.item(i)).getNodeType()!=Node.TEXT_NODE && ((Node)nodeList.item(i)).getNodeType()!=Node.COMMENT_NODE){
                            data[i] = ((Element)nodeList.item(i)).getTagName(); 
                            NamedNodeMap attributes = ((Element)nodeList.item(i)).getAttributes();
                            if (attributes.getLength()>0) data[i]=data[i]+"*";
                    }
            }                       
            
            return data;
    }
	
    /**
     * Creates a new text node and adds it to id position (removing id node if exists)
     * @param text New text node's content
     * @param id Position inside DOM tree
     */
    
    public void setTextNode(String text,NodeID id){
    	int last = id.getLast();
    	id.getList().remove(id.getList().size()-1);
    	DomNode tmp = this.getNode(id);
    	tmp.setTextValue(text, last);
    }
    
    public DomNode getNode(String path) {
    	DomNode result = null;
    	
    	// setup the XPath related objects
    	XPath xpath = XPathFactory.newInstance().newXPath();
    	XPathExpression expression;
    	 
    	try {
    	  // compile the xpath
    	  expression = xpath.compile(path);
//    		expression = xpath.compile("//book[author='Neal Stephenson']/title/text()");
//    		expression = xpath.compile("/inventory[1]/book[1]");
    	  
    	  
    		Node expRes = (Node)expression.evaluate((Document)mTree, XPathConstants.NODE);
//    		if(expRes == null) {
//    			System.out.println("ExpRes es null");
//    		} else {
//    			System.out.println("ExpRes no es null");
//    		}
    	  
//    		result = new DomNode((Node)expression.evaluate(mTree, XPathConstants.NODE));
    		result = new DomNode(expRes);
    	  
    	} catch (javax.xml.xpath.XPathExpressionException ex) {
    	  ex.printStackTrace();
    	  //TODO error!
    	}
    	
    	
    	return result;
    }
    
}
